package com.guilin.system.service.impl;

import com.guilin.common.constant.CacheConstants;
import com.guilin.common.constant.Constants;
import com.guilin.common.core.redis.RedisCache;
import com.guilin.common.utils.StringUtils;
import com.guilin.domain.Alumni;
import com.guilin.system.service.QQToKenService;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class QQToKenServiceImpl implements QQToKenService {

    @Autowired
    private RedisCache redisCache;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; //1分钟

    // 令牌黑名单前缀
    private static final String TOKEN_BLACKLIST_PREFIX = CacheConstants.TOKEN_BLACKLIST_KEY;

    private static final String INVALID_TOKEN = "invalid";

    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    // 令牌有效期（默认60分钟)
    @Value("${token.expireTime}")
    private int expireTime;

    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    /**
     * 基于校友信息创建JWT令牌
     *
     * @param alumni
     * @return
     */
    public String createToken(Alumni alumni) {

        Map<String, Object> claims = new HashMap<>();
        if (alumni != null) {
            claims.put(Constants.ALUMNI_ID, alumni.getId());
            claims.put(Constants.ALUMNI_NAME, alumni.getUsername());
            claims.put(Constants.ALUMNI_ROLE, alumni.getAlumniRole());
            claims.put(Constants.ALUMNI_AVATAR, alumni.getAvatarUrl());
        }

        return createJWTToken(claims);
    }


    /**
     * 生成JWT令牌
     *
     * @param claims
     * @return
     */
    public String createJWTToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date())  // 设置签发时间为当前时间
                .setExpiration(new Date(System.currentTimeMillis() + expireTime * MILLIS_MINUTE)) // 设置过期时间
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }


    /**
     * 解析JWT令牌
     * @param token
     * @return
     */
    public Claims parseToken(String token) {

        try {
            return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            log.error("解析令牌异常", e);
            return null;
        }
    }

    /**
     * 验证JWT令牌
     * @param token
     * @return
     */
    public boolean verifyToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return false;
        }

        // 检查是否在黑名单中
        if (isTokenBlacklisted(token)) {
            return false;
        }

        try {
            Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token) // 解析并验证Token
                    .getBody();
            return true;
        } catch (MalformedJwtException e) {
            log.error("无效的JWT令牌");
        } catch (ExpiredJwtException e) {
            log.error("JWT令牌已过期");
        } catch (UnsupportedJwtException e) {
            log.error("不支持的JWT令牌");
        } catch (IllegalArgumentException e) {
            log.error("JWT令牌参数异常");
        }
        return false;
    }


    /**
     * 刷新JWT令牌
     * @param oldToken
     * @return
     */
    public String refreshToken(String oldToken) {

        //验证旧令牌
        if (!verifyToken(oldToken)) {
            throw new IllegalArgumentException("无效的令牌，无法刷新");
        }

        //解析旧令牌
        Claims claims = parseToken(oldToken);
        claims.setIssuedAt(new Date());  // 设置签发时间为当前时间
        claims.setExpiration(new Date(System.currentTimeMillis() + expireTime * MILLIS_MINUTE)); // 设置过期时间
        // 生成新令牌
        return Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();

    }


    /**
     * 使令牌失效
     * @param token
     */
    public void invalidateToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return;
        }

        try {
            Claims claims = parseToken(token);
            if (claims != null) {
                // 计算令牌剩余有效期
                Date expirationDate = claims.getExpiration();
                long remainingTime = expirationDate.getTime() - System.currentTimeMillis();

                if (remainingTime > 0) {
                    //将令牌加入黑名单，存储值为"invalid"，直到自然过期，过期时间为令牌剩余有效期
                    redisCache.setCacheObject(
                            TOKEN_BLACKLIST_PREFIX + token,
                            INVALID_TOKEN,
                            remainingTime,
                            TimeUnit.MILLISECONDS
                    );
                }
            }
        } catch (Exception e) {
            log.error("失效令牌异常", e);
        }
    }


    /**
     * 检查令牌是否在黑名单中
     * @param token
     * @return
     */
    private boolean isTokenBlacklisted(String token) {
        return redisCache.hasKey(TOKEN_BLACKLIST_PREFIX + token);
    }

    /**
     * 从令牌中获取校友role
     * @param token
     * @return
     */
    public String getAlumniRoleFromToken(String token) {
        Claims claims = parseToken(token);
        if (claims != null) {
            return claims.get(Constants.ALUMNI_ROLE, String.class);
        }
        return null;
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    public String getToken(HttpServletRequest request)
    {
        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
        {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }


    /**
     * 从令牌中获取校友ID
     * @param oldToken
     * @param newAvatarUrl
     * @return
     */
    public String updateAvatarInToken(String oldToken, String newAvatarUrl) {

        if (StringUtils.isEmpty(oldToken) || StringUtils.isEmpty(newAvatarUrl)) {
            return null;
        }

        try {
            // 1. 解析旧令牌
            Claims claims = parseToken(oldToken);
            if (claims == null) {
                log.error("解析旧令牌失败，无法更新头像");
                return null;
            }

            // 2. 更新头像URL
            claims.put(Constants.ALUMNI_AVATAR, newAvatarUrl);

            // 3. 生成新令牌（保持原有过期时间）
            return Jwts.builder()
                    .setClaims(claims)
                    .setIssuedAt(new Date())  // 更新签发时间
                    .setExpiration(claims.getExpiration())  // 保持原过期时间
                    .signWith(SignatureAlgorithm.HS256, secret)
                    .compact();
        } catch (Exception e) {
            log.error("更新令牌头像信息异常", e);
            return null;
        }
    }

    /**
     * 使旧令牌失效
     */
    public void invalidateOldToken(String oldToken) {
        if (StringUtils.isEmpty(oldToken)) {
            return;
        }
        invalidateToken(oldToken);
    }



}
